import utils from '../utils'
import urls from '../urls';

export default class Model {

    /**配置中的urls 方便子类使用 */
    $urls = urls

    _id = ""

    $config = {
        /**创建api */
        $create: null,
        /**更新api */
        $update: null,
        /**删除api */
        $delete: null,
        /**读取api */
        $retrieve: null,
        /**唯一标识字段，例如值为'Id' */
        id: null
    }


    constructor(options) {
        options = options || {};
        /**构造函数中可指定唯一标识字段名称 */
        if (options.id) this.$config.id = options.id;
        /**定义_id */
        this._id = options._id || this.genrateId();
    }

    /**生成临时Id */
    genrateId() {
        var p1 = Math.random() * 100000000;
        var p2 = Math.random() * 100000000;
        var p3 = Math.random() * 100000000;
        var p4 = Math.random() * 100000000;
        return `${Math.round(p1)}-${Math.round(p2)}-${Math.round(p3)}-${Math.round(p4)}`;
    }

    /**判断参数是不是函数 */
    isFunction(obj) {
        return Object.prototype.toString.call(obj) === '[object Function]';
    }

    /**判断参数是不是对象 */
    isObject(obj) {
        return Object.prototype.toString.call(obj) === '[object Object]';
    }

    /**判断参数是不是对象 */
    isArray(obj) {
        return Object.prototype.toString.call(obj) === '[object Array]';
    }

    /**把参数的字段数据赋值给当前类，要求字段名称一致 */
    assign(model) {
        if (!this.isObject(model)) return;

        let myPublicField = [];

        for (var key in this) {
            /**以下划线开头的定义为私有变量 */
            if (key.indexOf('_') === 0) continue;
            /**$config不可以覆盖 */
            if (key === '$config') continue;
            /**方法过滤 */
            if (this.isFunction(this[key]) === '[object Function]') continue;
            myPublicField.push(key);
        }

        /**外来传值赋值给当前类的实例 */
        for (var key in model) {
            if (myPublicField.indexOf(key) === -1) continue;
            this[key] = model[key]
        }
        return this;
    }

    /**保存数据到服务器 */
    save(options) {
        options = options || {};
        if (!this.$config.id && !options.id) throw new Error('未绑定唯一标识字段名称[id]');
        if (!options.url && !this.$config.$create) throw new Error('未绑定新建数据api[$config.$create]');
        if (!options.url && !this.$config.$update) throw new Error('未绑定编辑数据api[$config.$update]');

        /**转换为纯粹的对象 */
        var data = this.convertPOJO(options.data) || this.convertPOJO();
        var config = this.$config;
        var url = options.url || (this[config.id] ? config.$update : config.$create);
        var vm = this;
        return new Promise((resolve, reject) => {
            var _options = {
                url: url,
                data: data,
                success: (res) => {
                    if (res && res.data && res.data.success === true)
                        resolve(arguments)
                    else
                        reject(arguments)
                },
                fail: () => reject(arguments)
            };
            utils.$post(_options);
        });
    }

    /**把自身转换成POJO */
    convertPOJO(obj) {
        obj = obj || this;
        var pojo = {};
        var key;
        for (key in obj) {
            /**以下划线开头的字段名称为私有变量 */
            if (key.indexOf('_') === 0) continue;
            /**以$开头的字段名称为私有变量 */
            if (key.indexOf('$') === 0) continue;
            /**以$开头的字段名称为接口配置 */
            if (key === '$config') continue;
            /**函数 */
            if (this.isFunction(obj[key])) continue;
            /**数组 */
            if (this.isArray(obj[key])) {
                var vm = this;
                var array = obj[key];
                var result = [];
                array.forEach(item => {
                    var child = vm.convertPOJO(item);
                    result.push(child);
                })
                obj[key] = result;
            }

            if (this.isObject(obj[key])) {
                pojo[key] = this.convertPOJO(obj[key])
            } else {
                pojo[key] = obj[key]
            }
        }

        return pojo;
    }

    /**删除服务器model */
    destory(options) {
        options = options || {};
        if (!this.$config.id && !options.id) throw new Error('未绑定唯一标识字段名称[id]');
        if (!options.url && !this.$config.$delete) throw new Error('未绑定新建数据api[$config.$delete]');
        /**默认是个数组，后端需要配合写删除接口是批量删除接口，目的是为了复用 */
        var data = options.data || [this[$config.id]];
        var vm = this;
        return new Promise((resolve, reject) => {
            var _options = {
                url: vm.$config.$delete,
                data: data,
                success: (res) => {
                    if (res && res.data && res.data.success === true)
                        resolve(arguments)
                    else
                        reject(arguments)
                },
                fail: () => reject(arguments)
            };
            utils.$post(_options);
        });
    }

    /**从服务器中更新model */
    retrieve() {
        var options = arguments[0] || {};
        if (!this.$config.id && !options.id) throw new Error('未绑定唯一标识字段名称[id]');
        if (!options.url && !this.$config.$retrieve) throw new Error('未绑定新建数据api[$config.$retrieve]');

        if (!options.data) {
            var data = {};
            options.data = data[this.$config.id] = this[$config.id]
        }

        var vm = this;
        return new Promise((resolve, reject) => {
            var _options = {
                url: vm.$config.$delete,
                data: options.data,
                success: (res) => {
                    if (res && res.data && res.data.success === true) {
                        var model = res.data.result;
                        if (model.items && model.items.length > 0) {
                            /**通过分页接口获取数据的返回结果 */
                            model = model.items[0];
                        } else if (model.items && model.items.length === 0) {
                            /**通过分页接口获取数据,但数据不存在 */
                            reject(arguments);
                            return;
                        }
                        vm.assign(res.data.result)
                        resolve(arguments)
                    }
                    else
                        reject(arguments)
                },
                fail: () => reject(arguments)
            };
            utils.$post(_options);
        });
    }
}